package mashibing.class7;

import java.util.*;

/**
 * 执行错误
 * 一对arr[i]和op[i]就代表一个事件：
 * 用户号为arr[i]，op[i] == T就代表这个用户购买了一件商品
 * op[i] == F就代表这个用户退货了一件商品
 * 现在你作为电商平台负责人，你想在每一个事件到来的时候，
 * 都给购买次数最多的前K名用户颁奖。
 * 所以每个事件发生后，你都需要一个得奖名单（得奖区）。
 *
 * 得奖系统的规则：
 * 1，如果某个用户购买商品数为0，但是又发生了退货事件，
 *      则认为该事件无效，得奖名单和上一个事件发生后一致，例子中的5用户
 * 2，某用户发生购买商品事件，购买商品数+1，发生退货事件，购买商品数-1
 * 3，每次都是最多K个用户得奖，K也为传入的参数
 *       如果根据全部规则，得奖人数确实不够K个，那就以不够的情况输出结果
 * 4，得奖系统分为得奖区和候选区，任何用户只要购买数>0，
 *       一定在这两个区域中的一个
 * 5，购买数最大的前K名用户进入得奖区，
 *       在最初时如果得奖区没有到达K个用户，那么新来的用户直接进入得奖区
 * 6，如果购买数不足以进入得奖区的用户，进入候选区
 *
 * @author yujiafan
 * @version 1.0.0
 * @create 2023-04-13
 */
public class Class7_3_EveryStepShowBoss {

    public static class Customers{
        public int id;
        public int buy;
        public int enterTime;

        public Customers(int id, int buy, int enterTime) {
            this.id = id;
            this.buy = buy;
            this.enterTime = enterTime;
        }
    }

    public static class WaitComparator implements Comparator<Customers>{
        @Override
        public int compare(Customers o1, Customers o2) {
            return o1.buy != o2.buy ? (o2.buy - o1.buy) : (o1.enterTime - o2.enterTime);
        }
    }

    public static class DadComparator implements Comparator<Customers>{
        @Override
        public int compare(Customers o1, Customers o2) {
            return o1.buy != o2.buy ? (o1.buy - o2.buy) : (o1.enterTime - o2.enterTime);
        }
    }

    public static class WhoYouDad {
        public Map<Integer,Customers> customersMap;
        public Class7_2_Heap<Customers> waitHeap;
        public Class7_2_Heap<Customers> dadHeap;
        public int limit;

        public WhoYouDad(int limit) {
            this.customersMap = new HashMap<>();
            this.waitHeap = new Class7_2_Heap<>(new WaitComparator());
            this.dadHeap = new Class7_2_Heap<>(new DadComparator());
            this.limit = limit;
        }

        public void operator(int id,boolean isBuy,int time){
            if(!customersMap.containsKey(id) && !isBuy){
                return;
            }
            if(!customersMap.containsKey(id)){
                customersMap.put(id,new Customers(id,0,0));
            }
            Customers customers = customersMap.get(id);
            if(isBuy){
                customers.buy = customers.buy + 1;
            } else {
                customers.buy = customers.buy - 1;
            }
            if(customers.buy == 0){
                customersMap.remove(id);
            }
            // id > 0,buy > 0,
            if(!waitHeap.contains(customers) && !dadHeap.contains(customers)){
                if(dadHeap.getSize() < limit){
                    customers.enterTime = time;
                    dadHeap.push(customers);
                } else {
                    customers.enterTime = time;
                    waitHeap.push(customers);
                }
            } else if(waitHeap.contains(customers)){
                if(customers.buy == 0){
                    waitHeap.remove(customers);
                } else {
                    waitHeap.resign(customers);
                }
            } else {
                if(customers.buy == 0){
                    dadHeap.remove(customers);
                } else {
                    dadHeap.resign(customers);
                }
            }
            daddyMove(time);
        }

        private void daddyMove(int time) {
            if(waitHeap.isEmpty()){
                return;
            }
            if (dadHeap.getSize() < limit) {
                Customers p = waitHeap.pop();
                p.enterTime = time;
                dadHeap.push(p);
            } else {
                if(waitHeap.peek().buy > dadHeap.peek().buy){
                    Customers w1 = waitHeap.pop();
                    Customers d1 = dadHeap.pop();
                    w1.enterTime = time;
                    d1.enterTime = time;
                    dadHeap.push(w1);
                    waitHeap.push(d1);
                }
            }
        }

        public List<Integer> getOneAns(){
            List<Customers> allElements = dadHeap.getAllElements();
            List<Integer> list = new ArrayList<>();
            for (Customers allElement : allElements) {
                list.add(allElement.id);
            }
            return list;
        }
    }

    public static List<List<Integer>> topK(int[] arr, boolean[] op, int k){
        List<List<Integer>> ans = new ArrayList<>();
        WhoYouDad whoYouDad = new WhoYouDad(k);
        for (int i = 0; i < arr.length; i++) {
            whoYouDad.operator(arr[i],op[i],k);
            ans.add(whoYouDad.getOneAns());
        }
        return ans;
    }
}
